import { storage, StorageKeychain } from '@/core';
import { Observable, Subject } from 'rxjs';
import { Environment } from '@/environments/environment';

import { isNullOrUndefined } from 'util';

class RequestParams {
  public method: 'GET' | 'POST' | 'DELETE' | 'PUT';
  public url = '';
  public data: object | string;
  public header: object = {};
  public tokenRequired = false;
}

class Http {
  public _header = {
    'Content-Type': 'application/json; charset=utf-8',
    'Accept': 'application/JSON',
  };
  private refreshTokenSubject: Subject<any> = new Subject<any>();
  private isRefreshing = false;

  private authorizationFailureSubject: Subject<any> = new Subject<any>();
  public get authorizationFailure(): Observable<any> {
    return this.authorizationFailureSubject.asObservable();
  }

  /* 服务器时间戳 */
  public get timestamp(): number {
    return new Date().getTime() / 1000;
  }

  public request(params: RequestParams): Observable<any> {
    return Observable.create((observer: any) => {
      const header: any = {
        ...this._header,
        ...params.header,
      };
      if (!header.Authorization) {
        header.Authorization = 'token ' + storage.get(StorageKeychain.AuthAccessToken);
      }
      // const url = params.url;
      Taro.request({
        url: this.processRequestUrl(params.url),
        method: params.method,
        data: params.data || {},
        header,
        success: (res: any) => {
          if (res.statusCode >= 200 && res.statusCode < 300) {
            observer.next(res);
            observer.complete();
            return;
          }
          if (params.tokenRequired && Object.is(res.statusCode, 403) && storage.get(StorageKeychain.CurrentLoginMobile)) {
            const subscription = this.refreshToken().subscribe((err) => {
              subscription.unsubscribe();
              if (err) {
                this.authorizationFailureSubject.next();
                observer.error(err);
                observer.complete();
              } else {
                const method = params.method.toLowerCase();
                if (header['X-UU-TOKEN']) {
                  header['X-UU-TOKEN'] = 'token ' + storage.get(StorageKeychain.UUAccessToken);
                }
                if (header.type) {
                  header['Authorization'] = 'token ' + storage.get(StorageKeychain.UUAccessToken);
                } else {
                  header['Authorization'] = 'token ' + storage.get(StorageKeychain.AuthAccessToken);
                }
                console.log(params.url);
                this[method](this.processRequestUrl(params.url), params.data, header, params.tokenRequired).subscribe((data: any) => {
                  observer.next(data);
                  observer.complete();
                }, (errC: any) => {
                  observer.error(errC);
                });
              }
            });
          } else {
            observer.error(res);
            observer.complete();
          }
        },
        fail: (err: any) => {
          observer.error(err);
          observer.complete();
        },
      });
    });
  }

  public get(url: string, data?: any, header: any = {}, tokenRequired: boolean = true, serialize: boolean = true) {
    if (serialize) {
      if (this.serialize(data)) {
        url += '?' + this.serialize(data);
      }
    }
    return this.request({ url, method: 'GET', header, data, tokenRequired });
  }

  public post(url: string, data?: any, header: any = {}, tokenRequired: boolean = true) {
    return this.request({ url, method: 'POST', header, data, tokenRequired });
  }

  public put(url: string, data?: any, header: any = {}, tokenRequired: boolean = true) {
    return this.request({ url, method: 'PUT', header, data, tokenRequired });
  }

  public delete(url: string, data?: any, header: any = {}, tokenRequired: boolean = true) {
    return this.request({ url, method: 'DELETE', header, data, tokenRequired });
  }

  public postForm(url: string, data?: any, header: any = {}, tokenRequired: boolean = true, serialize: boolean = true) {
    const tempHeader = header ? header : {};
    let tempData = data;
    if (serialize) {
      tempData = this.serialize(data);
    }
    tempHeader['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
    // tempHeader.Authorization =  'Basic ' + 'NjJkMDc1MmM1MGUyMTFlN2JiZjQwMjQyYWMxNzA3MDI6MWQwYTg1NzkzZGZlNDNmZWEwYzFkMWVhOGNiYmFkM2U=',
    // tempHeader.code = '62d0752c50e211e7bbf40242ac170702';
    return this.request({ url, method: 'POST', header: tempHeader, data: tempData, tokenRequired });
  }

  public uploadImg(url: string, filePath: any, name: any, formData?: any, header: any = {}, tokenRequired: boolean = true) {
    const header_authorization = {
      'Authorization': 'token ' + storage.get(StorageKeychain.AuthAccessToken),
      'Content-Type': 'multipart/form-data',
      ...header,
    };
    return Observable.create((observe: any) => {
      Taro.uploadFile({
        url: this.processRequestUrl(url),
        filePath,
        name,
        formData,
        header: { ...header_authorization },
        success: (res) => {
          if (res.statusCode >= 200 && res.statusCode < 300) {
            const result = JSON.parse(res.data);
            observe.next(result);
            observe.complete();
          } else {
            if (Object.is(res.statusCode, 403) && storage.get(StorageKeychain.CurrentLoginMobile)) {
              this.refreshToken().subscribe((err) => {
                if (err) {
                  this.authorizationFailureSubject.next();
                } else {
                  if (header['X-UU-TOKEN']) {
                    header['X-UU-TOKEN'] = 'token ' + storage.get(StorageKeychain.UUAccessToken);
                  }
                  if (header.type) {
                    header['Authorization'] = 'token ' + storage.get(StorageKeychain.UUAccessToken);
                  } else {
                    header['Authorization'] = 'token ' + storage.get(StorageKeychain.AuthAccessToken);
                  }
                  this.uploadImg(url, filePath, name, formData, header, tokenRequired).subscribe((result: any) => {
                    observe.next(result);
                    observe.complete();
                  }, (errC: any) => {
                    observe.error(errC);
                  });
                }
              });
            } else {
              observe.error(res);
              observe.complete();
            }
          }
        }, fail: (err) => {
          observe.error(err);
          observe.complete();
        },
      });
    });
  }

  public refreshToken() {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      // 授权失败时先刷新uuToken
      const uuUrl = Environment.UU_WXMP_DOMAIN + '/mp/oauth/2.0/token';
      const uuBody = {
        grant_type: 'wechat',
        client_id: Environment.UUClientId,
        client_secret: Environment.UUClientSecret,
        app_code: 'sy_wxmp',
        mp_openid: storage.get(StorageKeychain.OpenId),
      };
      this.postForm(uuUrl, uuBody, null, false).subscribe(
        (uuData: any) => {
          // 刷新成功继续刷新htToken
          const htUrl = Environment.AUTH_DOMAIN + '/oauth/2.0/token';
          const htBody = {
            grant_type: 'uu_token',
            client_id: Environment.AuthClientId,
            client_secret: Environment.AuthClientSecret,
            token: uuData.data.access_token,
          };
          this.postForm(htUrl, htBody, null, false).subscribe(
            (htData: any) => {
              const ht_token = htData.data.access_token;
              const uu_token = uuData.data.access_token;

              // 缓存token信息
              storage.set(StorageKeychain.WxUserIds, JSON.stringify({ ht_user_id: htData.data.userid, uu_user_id: uuData.data.userid }));
              storage.set(StorageKeychain.AuthAccessToken, ht_token);
              storage.set(StorageKeychain.UUAccessToken, uu_token);
              this.refreshTokenSubject.next(null);
              this.isRefreshing = false;
            },
            (errB: any) => {
              storage.remove(StorageKeychain.AuthAccessToken);
              storage.remove(StorageKeychain.UUAccessToken);
              this.refreshTokenSubject.next(errB);
              this.isRefreshing = false;
            },
          );
        },
        (errA: any) => {
          storage.remove(StorageKeychain.AuthAccessToken);
          storage.remove(StorageKeychain.UUAccessToken);
          this.refreshTokenSubject.next(errA);
          this.isRefreshing = false;
        },
      );
    }
    return this.refreshTokenSubject.asObservable();
  }

  /**
   * 辅助方法，生成请求检索参数,适用于params请求，非body
   * @param objParams 简单的obj对象，非实例
   * @returns {URLSearchParams}
   */
  public generateURLSearchParams(objParams: any) {
    const searchParams = {};

    if (objParams) {
      Object.keys(objParams).forEach(key => {
        searchParams[key] = !isNullOrUndefined(objParams[key])
          ? objParams[key]
          : '';
      });
    }

    return searchParams;
  }

  private serialize(param: any) {
    if (!param) {
      return '';
    }
    let paramString = '';
    Object.keys(param).forEach((key) => {
      paramString += `&${key}=${param[key]}`;
    });
    return '' || paramString.substring(1);
  }

  private processRequestUrl(url: string): string {
    //  勿动
    // return url;
    return `${Environment.MAIN_DOMAIN}/${url.replace(/^(https:)?\/\//, '').replace(/^(http:)?\/\//, '')}`;
  }
}


export abstract class LinkResponse {

  public results: any[];
  public linkUrl: string;

  constructor(httpResponse: any) {
    const results = httpResponse.data;
    const linkInfo = httpResponse.header && httpResponse.header.Link;
    if (linkInfo) {
      this.linkUrl = linkInfo.split('>')[0].split('<')[1];
    }
    this.results = this.generateEntityData(results);
  }

  public abstract generateEntityData(results: any[]): any[];
}

const http = new Http();
export default http;
